package org.wat.wcy.isi.mmazur.bp.io.xpdl;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.wat.wcy.isi.mmazur.bp.io.core.IDataProvider;
import org.wat.wcy.isi.mmazur.bp.io.model.Node;
import org.wat.wcy.isi.mmazur.bp.io.model.Transition;
import org.wat.wcy.isi.mmazur.bp.io.model.temporary.Route;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;


public class XPDLHandler extends DefaultHandler implements IDataProvider {
	
	private IXMLProcessor docProcessor = null;
	
	private String logSpaces;
	private boolean elementStarted;
	private String charactersRead;
	private String currentPath;
	
	private Map<Route, Transition> transitonsMap;
	private Map<String, Node> nodesMap;
	private Map<String, Node> poolsMap;
	
	public XPDLHandler() {
		logSpaces = "";
		currentPath = "";
		transitonsMap = new HashMap<Route, Transition>();
		nodesMap = new HashMap<String, Node>();
		poolsMap = new HashMap<String, Node>();
	}

	@Override
	public void characters(char[] ch, int start, int length) throws SAXException {
		if (elementStarted) {
			charactersRead = new String(ch, start, length);
			charactersRead = charactersRead.trim();
//				System.out.println(logSpaces + charactersRead);
		}
	}

	@Override
	public void endDocument() throws SAXException {
		System.out.println("End document");
	}

	@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		if (charactersRead != null && isLogEnabled()) {
			System.out.println(logSpaces + charactersRead);
		}
		
		String name = StringUtils.isEmpty(localName) ? qName : localName;
		
		logSpaces = logSpaces.substring(1);
		if (isLogEnabled()) {
			System.out.println(logSpaces + "< " + name);
		}
		
		if (docProcessor != null) {
			docProcessor.endElement(currentPath, charactersRead);
		}
		
		releaseDocProcessorIfNeccessary(currentPath);
		
		currentPath = currentPath.substring(0, currentPath.lastIndexOf("/"));
		elementStarted = false;
	}
	

	private boolean isLogEnabled() {
		return false;
	}

	private void releaseDocProcessorIfNeccessary(String cpath) {
		if (docProcessor == null) {
			return;
		}
		for (XPDLProcessorFactory dp : XPDLProcessorFactory.values()) {
			if (StringUtils.equals(cpath, dp.pattern)) {
				Map<String, Node> nodes = docProcessor.getNodes();
				if (nodes != null) {
					nodesMap.putAll(nodes);
				}
				nodes = docProcessor.getPools();
				if (nodes != null) {
					poolsMap.putAll(nodes);
				}
				Map<Route, Transition> transitions = docProcessor.getTransitions();
				if (transitions != null) {
					transitonsMap.putAll(transitions);
				}
				docProcessor = null;
			}
		}
	}

	@Override
	public void error(SAXParseException err) throws SAXException {
		System.out.println("!Error " + err.getMessage());
		super.error(err);
	}

	@Override
	public void fatalError(SAXParseException err) throws SAXException {
		System.out.println("!Fatal Error " + err.getMessage());
		super.fatalError(err);
	}

	@Override
	public InputSource resolveEntity(String arg0, String arg1)
			throws IOException, SAXException {
		return super.resolveEntity(arg0, arg1);
	}

	@Override
	public void startDocument() throws SAXException {
		System.out.println("Document start");
	}

	@Override
	public void startElement(String uri, String localName, String qName,
			Attributes attrs) throws SAXException {
		String name = StringUtils.isEmpty(localName) ? qName : localName;
		currentPath += "/" + name;
		if (isLogEnabled()) {
		System.out.println(logSpaces + "> " + name);
		}
		int length = attrs.getLength();
		//Each attribute
		if (isLogEnabled()) {
			for (int i=0; i<length; i++) {
				// Get names and values to each attribute
				String attrName = attrs.getQName(i);
				String attrValue = attrs.getValue(i);
				System.out.format("%s@%s='%s'\n", logSpaces, attrName, attrValue);
			}
		}
		IXMLProcessor tmpDocProcessor = getDocProcessor(currentPath);
		if (tmpDocProcessor != null) {
			docProcessor = tmpDocProcessor;
		}
		if (docProcessor != null) {
			docProcessor.startElement(currentPath, attrs);
		}
		
		logSpaces += " ";
		elementStarted = true;
		charactersRead = null;
	}

	private IXMLProcessor getDocProcessor(String cpath) {
		for (XPDLProcessorFactory dp : XPDLProcessorFactory.values()) {
			if (StringUtils.equals(cpath, dp.pattern)) {
				return dp.getProcessor();
			}
		}
		return null;
	}

	@Override
	public void warning(SAXParseException err) throws SAXException {
		System.out.println("!Warning " + err.getMessage() );
		super.warning(err);
	}

	@Override
	public Map<String, Node> getNodes() {
		return nodesMap;
	}

	@Override
	public Map<Route, Transition> getTransitions() {
		return transitonsMap;
	}

	@Override
	public Map<String, Node> getPools() {
		return poolsMap;
	}
}